import java.awt.Color;
import java.awt.Point;
import java.util.Random;


public class Cluster implements Comparable<Cluster> {
	static final double DISTANCE_WEIGHT = 0.25;
	static final double COLOR_WEIGHT = 0.75;
	
	//The sum of the red components of pixels within this cluster
	private long m_redSum;
	//The sum of the green components of pixels within this cluster
	private long m_greenSum;
	//The sum of the blue components of pixels within this cluster
	private long m_blueSum;
	//The sum of the x components of the pixels within this cluster
	private long m_xSum;
	//The sum of the y components of the pixels within this cluster
	private long m_ySum;
	//The number of pixels represented by this cluster 
	private long m_area;
	//Tracks whether a pixel has been added since the last time the cluster
	//averages were recalculated
	private boolean m_dirty;
	private Color m_averageColor;
	private Point m_averagePosition;
	
	public Cluster()
	{
		m_averageColor = new Color(0, 0, 0);
		m_averagePosition = new Point(0, 0);
		clear();
	}
	
	//Resets this cluster, as if no pixels had ever been added to this cluster
	public void clear()
	{
		m_redSum = 0;
		m_greenSum = 0;
		m_blueSum = 0;
		m_xSum = 0;
		m_ySum = 0; 
		m_area = 0;
		m_dirty = true;	
	}
	
	//Adds the given pixel to this cluster
	public void addPixel(Point point, Color color)
	{
		m_redSum += color.getRed();
		m_greenSum += color.getGreen();
		m_blueSum += color.getBlue();
		m_xSum += point.getX();
		m_ySum += point.getY();
		m_area += 1;
		m_dirty = true;
	}
	
	private void recalculateAverages()
	{
		if (m_dirty)
		{
			if (m_area > 0)
			{
				int r = (int) (m_redSum / m_area);
				int g = (int) (m_greenSum / m_area);
				int b = (int) (m_blueSum / m_area);
				int x = (int) (m_xSum / m_area);
				int y = (int) (m_ySum / m_area);
				m_averageColor = new Color(r, g, b);
				m_averagePosition = new Point(x, y);
			}
			else
			{
				//Continue to use the last calculated position
				//Pick a random color.  Eventually a pixel will get picked up.
				Random rnd = new Random();
				int r = rnd.nextInt(256);
				int g = rnd.nextInt(256);
				int b = rnd.nextInt(256);
				m_averageColor = new Color(r, g, b);
			}
			m_dirty = false;
		}
	}
	
	//Returns a similarity metric between the given pixel and this cluster.
	//Smaller returned values mean the pixel is a better fit with this cluster.
	public double distance(Point point, Color color, int imageWidth,
			int imageHeight)
	{
		final double COLOR_MAX = 255;
		recalculateAverages();
		double dis = 0;
		dis += Math.pow(((point.getX() - m_averagePosition.getX())
				/ imageWidth), 2);
		dis += Math.pow(((point.getY() - m_averagePosition.getY())
				/ imageHeight), 2);
		dis += Math.pow(((color.getRed() - m_averageColor.getRed())
				/ COLOR_MAX), 2);
		dis += Math.pow(((color.getGreen() - m_averageColor.getGreen())
				/ COLOR_MAX), 2);
		dis += Math.pow(((color.getBlue() - m_averageColor.getBlue())
				/ COLOR_MAX), 2);
		dis = Math.sqrt(dis);
		return dis;
	}
	
	public double getX()
	{
		recalculateAverages();
		return m_averagePosition.getX();
	}
	
	public double getY()
	{
		recalculateAverages();
		return m_averagePosition.getY();
	}
	
	public double getRed()
	{
		recalculateAverages();
		return m_averageColor.getRed();
	}
	
	public double getGreen()
	{
		recalculateAverages();
		return m_averageColor.getGreen();
	}

	public double getBlue()
	{
		recalculateAverages();
		return m_averageColor.getBlue();
	}
	
	public Color getColor()
	{
		recalculateAverages();
		return m_averageColor;
	}
	
	public long getArea()
	{
		recalculateAverages();
		return m_area;
	}
	
	@Override
	public int compareTo(Cluster other)
	{
		return (int) (getX() - other.getX());
	}
}
